package org.nstack.core.componentloader;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.context.ApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.nstack.core.CoreGlobalProperty;
import org.nstack.header.exception.CloudRuntimeException;
import org.nstack.utils.logging.CLogger;
import org.nstack.utils.logging.CLoggerImpl;

import java.io.IOException;


public class ComponentLoaderImpl implements ComponentLoader {
    private static final CLogger logger = CLoggerImpl.getLogger(ComponentLoaderImpl.class);
    private final BeanFactory  ioc;
    private PluginRegistryIN pluginRegistry = null;
    private static boolean isInit = false;


    private void checkInit() {
        if (isInit) {
            throw new CloudRuntimeException("Nested ComponentLoader initialization detected. DO NOT call Platform.getComponentLoader() in bean's constructor, it causes nested initialization");
        }
        isInit = true;
    }
    
    public ComponentLoaderImpl(ApplicationContext appContext) {
        checkInit();
        ioc = appContext;
    }


    public ComponentLoaderImpl () throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
        checkInit();
        BeanFactoryLocator factoryLocator = ContextSingletonBeanFactoryLocator
                .getInstance(String.format("classpath:%s", CoreGlobalProperty.BEAN_REF_CONTEXT_CONF));
        BeanFactoryReference ref = factoryLocator.useBeanFactory("parentContext");
        ioc = ref.getFactory();
    }
    
    @Override
    public <T> T getComponent(Class<T> clazz) {
        return ioc.getBean(clazz);
    }

    @Override
    public <T> T getComponentNoExceptionWhenNotExisting(Class<T> clazz) {
        try {
            return getComponent(clazz);
        } catch (NoSuchBeanDefinitionException ne) {
            return null;
        }
    }

    @Override
    public <T> T getComponent(String className) {
        try {
            Class<?> clazz = Class.forName(className);
            return (T) getComponent(clazz);
        } catch (ClassNotFoundException e) {
            String err = "Cannot find bean with class " + className;
            throw new CloudRuntimeException(err, e);
        }
    }

    @Override
    public PluginRegistry getPluginRegistry() {
         
        if (pluginRegistry == null) {
            try {
                pluginRegistry = ioc.getBean(PluginRegistryIN.class);
            } catch (NoSuchBeanDefinitionException e) {
 
 
                logger.debug("if you are running unit test, this exception is safe. The reason is beans you load in unittest don't have  nstack plugin declaration, so PluginRegistry won't create", e);
                return null;
            }
            pluginRegistry.initialize();
        }
        return (PluginRegistry) pluginRegistry;
    }

    @Override
    public BeanFactory getSpringIoc() {
        return ioc;
    }

    @Override
    public <T> T getComponentByBeanName(String beanName) {
        return (T) ioc.getBean(beanName);
    }
}
